﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Configuration;
using System.Xml.Serialization;
using System.Xml;
using System.ComponentModel;

namespace GoodStuff
{
    public abstract class ConfigurationSectionHandler<T> : IConfigurationSectionHandler 
    {
        public object Create(object parent, object configContext, System.Xml.XmlNode section)
        {
            XmlSerializer ser = new XmlSerializer(typeof(T), new XmlRootAttribute(section.Name));
            XmlNodeReader reader = new XmlNodeReader(section);
            return ser.Deserialize(reader);
        }

        public static T GetConfiguration()
        {
            return ConfigurationHelper.GetSection<T>();
        }

        public static T GetConfiguration(string name)
        {
            return ConfigurationHelper.GetSection<T>(name);
        }
    }

    /// <summary>
    /// Configuration helper class that exposes generic
    /// section retrieval functionality
    /// </summary>
    public static class ConfigurationHelper
    {
        /// <summary>
        /// Retrieves a typed configuration section for
        /// the current application's default configuration
        /// </summary>
        /// <typeparam name="T">
        /// The type to bind to the configuration section</typeparam>
        /// <param name="sectionName">
        /// The name of the configuration section</param>
        /// <returns></returns>
        public static T GetSection<T>(string sectionName)
        {
            T section = (T)ConfigurationManager.GetSection(sectionName);
            if (section == null)
            {
                throw new ConfigurationErrorsException("Missing configuration section " + sectionName);
            }

            return section;
        }

        /// <summary>
        /// Retrieves a typed configuration section for
        /// the current application's default configuration
        /// </summary>
        /// <typeparam name="T">
        /// The type to bind to the configuration section</typeparam>
        /// <returns></returns>
        public static T GetSection<T>()
        {
            return GetSection<T>(typeof(T).Name);
        }


        public static T GetSetting<T>(string name, T defaultValue)
        {
            T result;
            if (TryGetSetting<T>(name, out result))
            {
                return result;
            }

            return defaultValue;
        }

        public static T GetSetting<T>(string name)
        {
            T result;
            if (TryGetSetting<T>(name, out result))
            {
                return result;
            }

            throw new ConfigurationErrorsException("ConfigurationHelper: Missing appsetting '" + name + "'");
        }

        public static bool TryGetSetting<T>(string name, out T result)
        {
            if (ConfigurationManager.AppSettings[name] != null)
            {
                string value = ConfigurationManager.AppSettings[name];
                try
                {
                    var foo = TypeDescriptor.GetConverter(typeof(T));
                    result = (T)(foo.ConvertFromInvariantString(value));
                 
                    return true;
                }
                catch (FormatException ee)
                {
                    throw new ConfigurationErrorsException("ConfigurationHelper: Invalid format for appsetting '" + name + "'", ee);
                }
            }
            result = default(T);
            return false;
        }

        [Obsolete("Use GetSetting() or TryGetSetting()", true)]
        public static T GetAppSetting<T>(string name, T defaultValue)
        {
            T result = GetAppSetting<T>(name, false);
            if (result.Equals(default(T)))
            {
                return defaultValue;
            }
            else
            {
                return result;
            }
        }

        [Obsolete("Use GetSetting() or TryGetSetting()", true)]
        public static T GetAppSetting<T>(string name)
        {
            return GetAppSetting<T>(name, true);
        }

        [Obsolete("Use GetSetting() or TryGetSetting()", true)]
        public static T GetAppSetting<T>(string name, bool throwOnMissing)
        {
            if (ConfigurationManager.AppSettings[name] == null)
            {
                if (throwOnMissing)
                {
                    throw new ConfigurationErrorsException("ConfigurationHelper: Missing appsetting '" + name + "'");
                }
                return default(T);
            }

            string value = ConfigurationManager.AppSettings[name];
            try
            {
                T result = (T)Convert.ChangeType(value, typeof(T));
                return result;
            }
            catch (FormatException ee)
            {
                throw new ConfigurationErrorsException("ConfigurationHelper: Invalid format for appsetting '" + name + "'", ee);
            }
        }

        /// <summary>
        /// Gets the default connection string with the default name 'Database'.
        /// </summary>
        /// <param name="connectionName"></param>
        /// <returns></returns>
        public static string GetConnectionString()
        {
            return GetConnectionString("Database");
        }

        public static string GetConnectionString(string connectionName)
        {
            CBP.Precondition(!string.IsNullOrEmpty(connectionName), "Expected a nonempty connection name");

            ConnectionStringSettings connectionSettings = System.Configuration.ConfigurationManager.ConnectionStrings[connectionName];
            if (connectionSettings == null)
            {
                throw new ConfigurationErrorsException("ConfigurationHelper: Cannot find connectionstring '" + connectionName + "'");
            }
            return connectionSettings.ConnectionString;
        }

        public static IEnumerable<string> GetConnectionStringNames()
        {
            return System.Configuration.ConfigurationManager.ConnectionStrings.Cast<ConnectionStringSettings>().Select(p => p.Name);
        }        
    }
}
